/*
 * Copyright 2019 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.eclipse.emf.ecore.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.xml.type.XMLTypePackage;
import org.eclipse.emf.ecore.xml.type.util.XMLTypeUtil;

/*
    Kogito - replace the regexp pattern used on the String.split methods
 */
public class BasicExtendedMetaData implements ExtendedMetaData {

    public static final String WORD_SPLIT_REGEXP = " ";

    protected String annotationURI;
    protected EPackage.Registry registry;
    protected EPackage.Registry demandRegistry;
    protected Map<EModelElement, Object> extendedMetaDataHolderCache;
    protected Map<EModelElement, EAnnotation> annotationMap;

    public BasicExtendedMetaData() {
        this(ANNOTATION_URI, EPackage.Registry.INSTANCE);
    }

    public BasicExtendedMetaData(EPackage.Registry registry) {
        this(ANNOTATION_URI, registry);
    }

    public BasicExtendedMetaData(String annotationURI, EPackage.Registry registry) {
        this(annotationURI, registry, null);
    }

    public BasicExtendedMetaData(String annotationURI, EPackage.Registry registry, Map<EModelElement, EAnnotation> annotationMap) {
        this.annotationURI = annotationURI.intern();
        this.registry = registry;
        this.demandRegistry = new org.eclipse.emf.ecore.impl.EPackageRegistryImpl();
        this.annotationMap = annotationMap;

        if (annotationURI != ANNOTATION_URI) {
            extendedMetaDataHolderCache = new HashMap<EModelElement, Object>();
        }
    }

    protected EAnnotation getAnnotation(EModelElement eModelElement, boolean demandCreate) {
        if (annotationMap != null) {
            EAnnotation result = annotationMap.get(eModelElement);
            if (result == null && demandCreate) {
                result = EcoreFactory.eINSTANCE.createEAnnotation();
                result.setSource(annotationURI);
                annotationMap.put(eModelElement, result);
            }
            return result;
        } else {
            EAnnotation result = eModelElement.getEAnnotation(annotationURI);
            if (result == null && demandCreate) {
                result = EcoreFactory.eINSTANCE.createEAnnotation();
                result.setSource(annotationURI);
                eModelElement.getEAnnotations().add(result);
            }
            return result;
        }
    }

    public EClassifier getType(EPackage ePackage, String name) {
        return getExtendedMetaData(ePackage).getType(name);
    }

    public EPackage getPackage(String namespace) {
        EPackage ePackage = registry.getEPackage(namespace);
/*
    if (ePackage == null)
    {
      ePackage = demandRegistry.getEPackage(namespace);
    }
*/
        return ePackage;
    }

    public void putPackage(String namespace, EPackage ePackage) {
        registry.put(namespace, ePackage);
    }

    public EClass getDocumentRoot(EPackage ePackage) {
        return (EClass) getType(ePackage, "");
    }

    public void setDocumentRoot(EClass eClass) {
        setName(eClass, "");
        setContentKind(eClass, MIXED_CONTENT);
    }

    public boolean isDocumentRoot(EClass eClass) {
        return "".equals(getName(eClass));
    }

    public EReference getXMLNSPrefixMapFeature(EClass eClass) {
        if (getContentKind(eClass) == MIXED_CONTENT) {
            List<EReference> eAllReferences = eClass.getEAllReferences();
            for (int i = 0, size = eAllReferences.size(); i < size; ++i) {
                EReference eReference = eAllReferences.get(i);
                if ("xmlns:prefix".equals(getName(eReference))) {
                    return eReference;
                }
            }
        }

        return null;
    }

    public EReference getXSISchemaLocationMapFeature(EClass eClass) {
        if (getContentKind(eClass) == MIXED_CONTENT) {
            List<EReference> eAllReferences = eClass.getEAllReferences();
            for (int i = 0, size = eAllReferences.size(); i < size; ++i) {
                EReference eReference = eAllReferences.get(i);
                if ("xsi:schemaLocation".equals(getName(eReference))) {
                    return eReference;
                }
            }
        }

        return null;
    }

    public boolean isQualified(EPackage ePackage) {
        return getExtendedMetaData(ePackage).isQualified();
    }

    protected boolean basicIsQualified(EPackage ePackage) {
        EAnnotation eAnnotation = getAnnotation(ePackage, false);
        return eAnnotation == null || !"false".equals(eAnnotation.getDetails().get("qualified"));
    }

    public void setQualified(EPackage ePackage, boolean isQualified) {
        if (!isQualified) {
            EAnnotation eAnnotation = getAnnotation(ePackage, true);
            eAnnotation.getDetails().put("qualified", "false");
        } else {
            EAnnotation eAnnotation = getAnnotation(ePackage, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("qualified");
            }
        }
        getExtendedMetaData(ePackage).setQualified(isQualified);
    }

    public String getNamespace(EPackage ePackage) {
        if (isQualified(ePackage)) {
            return (ePackage).getNsURI();
        } else {
            return null;
        }
    }

    public String getNamespace(EClassifier eClassifier) {
        return getNamespace(eClassifier.getEPackage());
    }

    public String getNamespace(EStructuralFeature eStructuralFeature) {
        return getExtendedMetaData(eStructuralFeature).getNamespace();
    }

    public String basicGetNamespace(EStructuralFeature eStructuralFeature) {
        EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
        if (eAnnotation == null) {
            return null;
        } else {
            String result = eAnnotation.getDetails().get("namespace");
            if ("##targetNamespace".equals(result)) {
                return getNamespace(eStructuralFeature.getEContainingClass().getEPackage());
            } else {
                return result;
            }
        }
    }

    public void setNamespace(EStructuralFeature eStructuralFeature, String namespace) {
        String packageNamespace = getNamespace(eStructuralFeature.getEContainingClass().getEPackage());
        String convertedNamespace = namespace;
        if (namespace == null ? packageNamespace == null : namespace.equals(packageNamespace)) {
            convertedNamespace = "##targetNamespace";
        }

        if (convertedNamespace != null) {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, true);
            eAnnotation.getDetails().put("namespace", convertedNamespace);
        } else {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("namespace");
            }
        }
        getExtendedMetaData(eStructuralFeature).setNamespace(namespace);
    }

    public String getName(EClassifier eClassifier) {
        return getExtendedMetaData(eClassifier).getName();
    }

    protected String basicGetName(EClassifier eClassifier) {
        EAnnotation eAnnotation = getAnnotation(eClassifier, false);
        if (eAnnotation != null) {
            String result = eAnnotation.getDetails().get("name");
            if (result != null) {
                return result;
            }
        }
        return eClassifier.getName();
    }

    public void setName(EClassifier eClassifier, String name) {
        EAnnotation eAnnotation = getAnnotation(eClassifier, true);
        eAnnotation.getDetails().put("name", name);
        getExtendedMetaData(eClassifier).setName(name);
        EPackage ePackage = eClassifier.getEPackage();
        if (ePackage != null) {
            getExtendedMetaData(ePackage).rename(eClassifier, name);
        }
    }

    public boolean isAnonymous(EClassifier eClassifier) {
        String name = getExtendedMetaData(eClassifier).getName();
        return name.length() == 0 || name.indexOf("_._") != -1;
    }

    public String getName(EStructuralFeature eStructuralFeature) {
        return getExtendedMetaData(eStructuralFeature).getName();
    }

    protected String basicGetName(EStructuralFeature eStructuralFeature) {
        EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
        if (eAnnotation != null) {
            String result = eAnnotation.getDetails().get("name");
            if (result != null) {
                return result;
            }
        }
        return eStructuralFeature.getName();
    }

    public void setName(EStructuralFeature eStructuralFeature, String name) {
        EAnnotation eAnnotation = getAnnotation(eStructuralFeature, true);
        eAnnotation.getDetails().put("name", name);
        getExtendedMetaData(eStructuralFeature).setName(name);
    }

    protected String getQualifiedName(String defaultNamespace, EClassifier eClassifier) {
        String namespace = getNamespace(eClassifier);
        String name = getName(eClassifier);
        if (namespace == null) {
            return namespace == defaultNamespace ? name : "#" + name;
        } else {
            return namespace.equals(defaultNamespace) ? name : namespace + "#" + name;
        }
    }

    protected String getQualifiedName(String defaultNamespace, EStructuralFeature eStructuralFeature) {
        String namespace = getNamespace(eStructuralFeature);
        String name = getName(eStructuralFeature);
        if (namespace == null) {
            return namespace == defaultNamespace ? name : "#" + name;
        } else {
            return namespace.equals(defaultNamespace) ? name : namespace + "#" + name;
        }
    }

    public EClassifier getType(String namespace, String name) {
        EPackage ePackage = getPackage(namespace);
        return ePackage == null ? null : getType(ePackage, name);
    }

    public EStructuralFeature getAttribute(String namespace, String name) {
        EPackage ePackage = getPackage(namespace);
        if (ePackage != null) {
            EClass documentRoot = getDocumentRoot(ePackage);
            if (documentRoot != null) {
                return getLocalAttribute(documentRoot, namespace, name);
            }
        }

        return null;
    }

    public EStructuralFeature getElement(String namespace, String name) {
        EPackage ePackage = getPackage(namespace);
        if (ePackage != null) {
            EClass documentRoot = getDocumentRoot(ePackage);
            if (documentRoot != null) {
                return getLocalElement(documentRoot, namespace, name);
            }
        }

        return null;
    }

    public int getFeatureKind(EStructuralFeature eStructuralFeature) {
        return getExtendedMetaData(eStructuralFeature).getFeatureKind();
    }

    protected int basicGetFeatureKind(EStructuralFeature eStructuralFeature) {
        EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
        if (eAnnotation != null) {
            Object kind = eAnnotation.getDetails().get("kind");
            if (kind != null) {
                for (int i = 1; i < FEATURE_KINDS.length; ++i) {
                    if (FEATURE_KINDS[i].equals(kind)) {
                        return i;
                    }
                }
            }
        }

        return 0;
    }

    public void setFeatureKind(EStructuralFeature eStructuralFeature, int kind) {
        if (kind > 0 && kind < FEATURE_KINDS.length) {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, true);
            eAnnotation.getDetails().put("kind", FEATURE_KINDS[kind]);
        } else {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("kind");
            }
        }
        getExtendedMetaData(eStructuralFeature).setFeatureKind(kind);
    }

    public int getContentKind(EClass eClass) {
        return getExtendedMetaData(eClass).getContentKind();
    }

    protected int basicGetContentKind(EClass eClass) {
        EAnnotation eAnnotation = getAnnotation(eClass, false);
        if (eAnnotation != null) {
            Object kind = eAnnotation.getDetails().get("kind");
            if (kind != null) {
                for (int i = 1; i < CONTENT_KINDS.length; ++i) {
                    if (CONTENT_KINDS[i].equals(kind)) {
                        return i;
                    }
                }
            }
        }

        return 0;
    }

    public void setContentKind(EClass eClass, int kind) {
        if (kind > 0 && kind < CONTENT_KINDS.length) {
            EAnnotation eAnnotation = getAnnotation(eClass, true);
            eAnnotation.getDetails().put("kind", CONTENT_KINDS[kind]);
        } else {
            EAnnotation eAnnotation = getAnnotation(eClass, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("kind");
            }
        }
        getExtendedMetaData(eClass).setContentKind(kind);
    }

    public int getDerivationKind(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getDerivationKind();
    }

    protected int basicGetDerivationKind(EClassifier eClassifier) {
        EAnnotation eAnnotation = getAnnotation(eClassifier, false);
        if (eAnnotation != null) {
            EMap<String, String> details = eAnnotation.getDetails();
            Object kind = details.get("restriction");
            if (kind != null) {
                return RESTRICTION_DERIVATION;
            }
            kind = details.get("list");
            if (kind != null) {
                return LIST_DERIVATION;
            }
            kind = details.get("union");
            if (kind != null) {
                return UNION_DERIVATION;
            }
        }

        return 0;
    }

    public EDataType getBaseType(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getBaseType();
    }

    public EDataType basicGetBaseType(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            EMap<String, String> details = eAnnotation.getDetails();
            String baseType = details.get("baseType");
            if (baseType != null) {
                int index = baseType.lastIndexOf("#");
                EClassifier type =
                        index == -1 ?
                                getType(eDataType.getEPackage(), baseType) :
                                index == 0 ?
                                        getType((String) null, baseType.substring(1)) :
                                        getType(baseType.substring(0, index), baseType.substring(index + 1));
                if (type instanceof EDataType) {
                    return (EDataType) type;
                }
            }
        }

        return null;
    }

    public void setBaseType(EDataType eDataType, EDataType baseType) {
        if (baseType == null) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("baseType");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("baseType", getQualifiedName(getNamespace(eDataType), baseType));
        }
        getExtendedMetaData(eDataType).setBaseType(baseType);
    }

    public EDataType getItemType(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getItemType();
    }

    protected EDataType basicGetItemType(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            EMap<String, String> details = eAnnotation.getDetails();
            String itemType = details.get("itemType");
            if (itemType != null) {
                int index = itemType.lastIndexOf("#");
                EClassifier type =
                        index == -1 ?
                                getType(eDataType.getEPackage(), itemType) :
                                index == 0 ?
                                        getType((String) null, itemType.substring(1)) :
                                        getType(itemType.substring(0, index), itemType.substring(index + 1));
                if (type instanceof EDataType) {
                    return (EDataType) type;
                }
            }
        }

        return null;
    }

    public void setItemType(EDataType eDataType, EDataType itemType) {
        if (itemType == null) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("itemType");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("itemType", getQualifiedName(getNamespace(eDataType), itemType));
        }
        getExtendedMetaData(eDataType).setItemType(itemType);
    }

    public List<EDataType> getMemberTypes(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getMemberTypes();
    }

    protected List<EDataType> basicGetMemberTypes(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String memberTypes = eAnnotation.getDetails().get("memberTypes");
            if (memberTypes != null) {
                List<EDataType> result = new ArrayList<EDataType>();
                for (String member : memberTypes.split(WORD_SPLIT_REGEXP)) {
                    int index = member.lastIndexOf("#");
                    EClassifier type =
                            index == -1 ?
                                    getType(eDataType.getEPackage(), member) :
                                    index == 0 ?
                                            getType((String) null, member.substring(1)) :
                                            getType(member.substring(0, index), member.substring(index + 1));
                    if (type instanceof EDataType) {
                        result.add((EDataType) type);
                    }
                }
                return result;
            }
        }

        return Collections.emptyList();
    }

    public void setMemberTypes(EDataType eDataType, List<EDataType> memberTypes) {
        if (memberTypes.isEmpty()) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("memberTypes");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            String namespace = getNamespace(eDataType);
            StringBuffer result = new StringBuffer();
            for (int i = 0, size = memberTypes.size(); i < size; ++i) {
                result.append(getQualifiedName(namespace, memberTypes.get(i)));
                result.append(' ');
            }
            eAnnotation.getDetails().put("memberTypes", result.substring(0, result.length() - 1));
        }
        getExtendedMetaData(eDataType).setMemberTypes(memberTypes);
    }

    protected boolean isFeatureKindSpecific() {
        return true;
    }

    protected boolean isFeatureNamespaceMatchingLax() {
        return false;
    }

    public EStructuralFeature getLocalAttribute(EClass eClass, String namespace, String name) {
        EStructuralFeature result = null;
        if (isFeatureKindSpecific()) {
            List<EStructuralFeature> allAttributes = getAllAttributes(eClass);
            for (int i = 0, size = allAttributes.size(); i < size; ++i) {
                EStructuralFeature eStructuralFeature = allAttributes.get(i);
                if (name.equals(getName(eStructuralFeature))) {
                    String featureNamespace = getNamespace(eStructuralFeature);
                    if (namespace == null) {
                        if (featureNamespace == null) {
                            return eStructuralFeature;
                        } else if (result == null) {
                            result = eStructuralFeature;
                        }
                    } else if (namespace.equals(featureNamespace)) {
                        return eStructuralFeature;
                    } else if (featureNamespace == null && result == null) {
                        result = eStructuralFeature;
                    }
                }
            }
        } else {
            for (int i = 0, size = eClass.getFeatureCount(); i < size; ++i) {
                EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(i);
                switch (getFeatureKind(eStructuralFeature)) {
                    case UNSPECIFIED_FEATURE:
                    case ATTRIBUTE_FEATURE: {
                        if (name.equals(getName(eStructuralFeature))) {
                            String featureNamespace = getNamespace(eStructuralFeature);
                            if (namespace == null) {
                                if (featureNamespace == null) {
                                    return eStructuralFeature;
                                } else if (result == null) {
                                    result = eStructuralFeature;
                                }
                            } else if (namespace.equals(featureNamespace)) {
                                return eStructuralFeature;
                            } else if (featureNamespace == null && result == null) {
                                result = eStructuralFeature;
                            }
                        }
                        break;
                    }
                }
            }
        }

        return isFeatureNamespaceMatchingLax() ? result : null;
    }

    public EStructuralFeature getAttribute(EClass eClass, String namespace, String name) {
        EStructuralFeature result = getLocalAttribute(eClass, namespace, name);
        if (result == null) {
            result = getAttribute(namespace, name);
            if (result != null && getAffiliation(eClass, result) == null) {
                return null;
            }
        }
        return result;
    }

    protected EStructuralFeature getLocalElement(EClass eClass, String namespace, String name) {
        EStructuralFeature result = null;
        if (isFeatureKindSpecific()) {
            List<EStructuralFeature> allElements = getAllElements(eClass);
            for (int i = 0, size = allElements.size(); i < size; ++i) {
                EStructuralFeature eStructuralFeature = allElements.get(i);
                if (name.equals(getName(eStructuralFeature))) {
                    String featureNamespace = getNamespace(eStructuralFeature);
                    if (namespace == null) {
                        if (featureNamespace == null) {
                            return eStructuralFeature;
                        } else if (result == null) {
                            result = eStructuralFeature;
                        }
                    } else if (namespace.equals(featureNamespace)) {
                        return eStructuralFeature;
                    } else if (featureNamespace == null && result == null) {
                        result = eStructuralFeature;
                    }
                }
            }
        } else {
            for (int i = 0, size = eClass.getFeatureCount(); i < size; ++i) {
                EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(i);
                switch (getFeatureKind(eStructuralFeature)) {
                    case UNSPECIFIED_FEATURE:
                    case ELEMENT_FEATURE: {
                        if (name.equals(getName(eStructuralFeature))) {
                            String featureNamespace = getNamespace(eStructuralFeature);
                            if (namespace == null) {
                                if (featureNamespace == null) {
                                    return eStructuralFeature;
                                } else if (result == null) {
                                    result = eStructuralFeature;
                                }
                            } else if (namespace.equals(featureNamespace)) {
                                return eStructuralFeature;
                            } else if (featureNamespace == null && result == null) {
                                result = eStructuralFeature;
                            }
                        }
                        break;
                    }
                }
            }
        }

        return isFeatureNamespaceMatchingLax() ? result : null;
    }

    public EStructuralFeature getElement(EClass eClass, String namespace, String name) {
        EStructuralFeature result = getLocalElement(eClass, namespace, name);
        if (result == null) {
            result = getElement(namespace, name);
            if (result != null && getAffiliation(eClass, result) == null) {
                return null;
            }
        }
        return result;
    }

    public List<EStructuralFeature> getAllAttributes(EClass eClass) {
        List<EClass> superTypes = eClass.getESuperTypes();
        List<EStructuralFeature> result = null;
        boolean changeable = false;
        for (int i = 0, size = superTypes.size(); i < size; ++i) {
            EClass eSuperType = superTypes.get(i);
            List<EStructuralFeature> allAttributes = getAllAttributes(eSuperType);
            if (!allAttributes.isEmpty()) {
                if (result == null) {
                    result = allAttributes;
                } else {
                    if (!changeable) {
                        changeable = true;
                        result = new UniqueEList<EStructuralFeature>(result);
                    }
                    result.addAll(allAttributes);
                }
            }
        }
        List<EStructuralFeature> attributes = getAttributes(eClass);
        if (!attributes.isEmpty()) {
            if (result == null) {
                return attributes;
            } else {
                if (!changeable) {
                    result = new UniqueEList<EStructuralFeature>(result);
                }
                result.addAll(attributes);
                return result;
            }
        } else {
            return result == null ? Collections.<EStructuralFeature>emptyList() : result;
        }
    }

    public List<EStructuralFeature> getAllElements(EClass eClass) {
        List<EClass> superTypes = eClass.getESuperTypes();
        List<EStructuralFeature> result = null;
        boolean changeable = false;
        for (int i = 0, size = superTypes.size(); i < size; ++i) {
            EClass eSuperType = superTypes.get(i);
            List<EStructuralFeature> allElements = getAllElements(eSuperType);
            if (!allElements.isEmpty()) {
                if (result == null) {
                    result = allElements;
                } else {
                    if (!changeable) {
                        changeable = true;
                        result = new UniqueEList<EStructuralFeature>(result);
                    }
                    result.addAll(allElements);
                }
            }
        }
        List<EStructuralFeature> elements = getElements(eClass);
        if (!elements.isEmpty()) {
            if (result == null) {
                return elements;
            } else {
                if (!changeable) {
                    result = new UniqueEList<EStructuralFeature>(result);
                }
                result.addAll(elements);
                return result;
            }
        } else {
            return result == null ? Collections.<EStructuralFeature>emptyList() : result;
        }
    }

    public List<EStructuralFeature> getAttributes(EClass eClass) {
        List<EStructuralFeature> eStructuralFeatures = eClass.getEStructuralFeatures();
        List<EStructuralFeature> result = null;
        for (int i = 0, size = eStructuralFeatures.size(); i < size; ++i) {
            EStructuralFeature eStructuralFeature = eStructuralFeatures.get(i);
            switch (getFeatureKind(eStructuralFeature)) {
                case ATTRIBUTE_FEATURE:
                case ATTRIBUTE_WILDCARD_FEATURE: {
                    if (result == null) {
                        result = new ArrayList<EStructuralFeature>();
                    }
                    result.add(eStructuralFeature);
                }
            }
        }
        return result == null ? Collections.<EStructuralFeature>emptyList() : result;
    }

    public List<EStructuralFeature> getElements(EClass eClass) {
        List<EStructuralFeature> eStructuralFeatures = eClass.getEStructuralFeatures();
        List<EStructuralFeature> result = null;
        for (int i = 0, size = eStructuralFeatures.size(); i < size; ++i) {
            EStructuralFeature eStructuralFeature = eStructuralFeatures.get(i);
            switch (getFeatureKind(eStructuralFeature)) {
                case ELEMENT_FEATURE:
                case ELEMENT_WILDCARD_FEATURE:
                case GROUP_FEATURE: {
                    if (result == null) {
                        result = new ArrayList<EStructuralFeature>();
                    }
                    result.add(eStructuralFeature);
                    break;
                }
            }
        }

        return result == null ? Collections.<EStructuralFeature>emptyList() : result;
    }

    public EStructuralFeature getSimpleFeature(EClass eClass) {
        if (getContentKind(eClass) == SIMPLE_CONTENT) {
            for (int i = 0, size = eClass.getFeatureCount(); i < size; ++i) {
                EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(i);
                if (getFeatureKind(eStructuralFeature) == ExtendedMetaData.SIMPLE_FEATURE) {
                    return eStructuralFeature;
                }
            }
        }

        return null;
    }

    public EAttribute getMixedFeature(EClass eClass) {
        switch (getContentKind(eClass)) {
            case MIXED_CONTENT:
            case SIMPLE_CONTENT: {
                List<EAttribute> eAllAttributes = eClass.getEAllAttributes();
                for (int i = 0, size = eAllAttributes.size(); i < size; ++i) {
                    EAttribute eAttribute = eAllAttributes.get(i);
                    if (getFeatureKind(eAttribute) == ExtendedMetaData.ELEMENT_WILDCARD_FEATURE) {
                        return eAttribute;
                    }
                }
                break;
            }
        }

        return null;
    }

    public List<String> getWildcards(EStructuralFeature eStructuralFeature) {
        return getExtendedMetaData(eStructuralFeature).getWildcards();
    }

    protected List<String> basicGetWildcards(EStructuralFeature eStructuralFeature) {
        EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
        if (eAnnotation != null) {
            String wildcards = eAnnotation.getDetails().get("wildcards");
            if (wildcards != null) {
                List<String> result = new ArrayList<String>();
                for (String wildcard : wildcards.split(WORD_SPLIT_REGEXP)) {
                    if (wildcard.equals("##other")) {
                        result.add("!##" + getNamespace(eStructuralFeature.getEContainingClass().getEPackage()));
                    } else if (wildcard.equals("##local")) {
                        result.add(null);
                    } else if (wildcard.equals("##targetNamespace")) {
                        result.add(getNamespace(eStructuralFeature.getEContainingClass().getEPackage()));
                    } else {
                        result.add(wildcard);
                    }
                }
                return result;
            }
        }

        return Collections.emptyList();
    }

    public void setWildcards(EStructuralFeature eStructuralFeature, List<String> wildcards) {
        if (wildcards.isEmpty()) {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("wildcards");
                eAnnotation.getDetails().remove("name");
            }
        } else {
            String namespace = getNamespace(eStructuralFeature.getEContainingClass().getEPackage());
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, true);
            eAnnotation.getDetails().put("wildcards", getEncodedWildcards(namespace, wildcards));
            eAnnotation.getDetails().put("name", "");
        }
        getExtendedMetaData(eStructuralFeature).setWildcards(wildcards);
    }

    public static String getEncodedWildcards(String namespace, List<String> wildcards) {
        if (wildcards.isEmpty()) {
            return "";
        } else {
            StringBuffer value = new StringBuffer();
            for (int i = 0, size = wildcards.size(); i < size; ) {
                String wildcard = wildcards.get(i);
                if (wildcard == null) {
                    if (namespace == null) {
                        value.append("##targetNamespace");
                    } else {
                        value.append("##local");
                    }
                } else if (wildcard.startsWith("!##")) {
                    if (namespace == null ?
                            wildcard.length() == 3 :
                            wildcard.endsWith(namespace) && wildcard.length() == namespace.length() + 3) {
                        value.append("##other");
                    } else {
                        value.append(wildcard);
                    }
                } else if (wildcard.equals(namespace)) {
                    value.append("##targetNamespace");
                } else {
                    value.append(wildcard);
                }

                if (++i < size) {
                    value.append(' ');
                }
            }
            return value.toString();
        }
    }

    public int getProcessingKind(EStructuralFeature eStructuralFeature) {
        return getExtendedMetaData(eStructuralFeature).getProcessingKind();
    }

    protected int basicGetProcessingKind(EStructuralFeature eStructuralFeature) {
        EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
        if (eAnnotation != null) {
            Object kind = eAnnotation.getDetails().get("processing");
            if (kind != null) {
                for (int i = 1; i < PROCESSING_KINDS.length; ++i) {
                    if (PROCESSING_KINDS[i].equals(kind)) {
                        return i;
                    }
                }
            }
        }

        return 0;
    }

    public void setProcessingKind(EStructuralFeature eStructuralFeature, int kind) {
        if (kind > 0 && kind < PROCESSING_KINDS.length) {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, true);
            eAnnotation.getDetails().put("processing", PROCESSING_KINDS[kind]);
        } else {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("processing");
            }
        }
        getExtendedMetaData(eStructuralFeature).setProcessingKind(kind);
    }

    public EStructuralFeature getGroup(EStructuralFeature eStructuralFeature) {
        return getExtendedMetaData(eStructuralFeature).getGroup();
    }

    protected EStructuralFeature basicGetGroup(EStructuralFeature eStructuralFeature) {
        EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
        if (eAnnotation != null) {
            String qualifiedName = eAnnotation.getDetails().get("group");
            if (qualifiedName != null) {
                int fragmentIndex = qualifiedName.lastIndexOf('#');
                if (fragmentIndex == -1) {
                    return
                            getElement
                                    (eStructuralFeature.getEContainingClass(),
                                     getNamespace(eStructuralFeature.getEContainingClass().getEPackage()),
                                     qualifiedName);
                } else if (fragmentIndex == 0) {
                    return
                            getElement
                                    (eStructuralFeature.getEContainingClass(),
                                     null,
                                     qualifiedName.substring(1));
                } else {
                    return
                            getElement
                                    (eStructuralFeature.getEContainingClass(),
                                     qualifiedName.substring(0, fragmentIndex),
                                     qualifiedName.substring(fragmentIndex + 1));
                }
            }
        }
        return null;
    }

    public void setGroup(EStructuralFeature eStructuralFeature, EStructuralFeature group) {
        if (group == null) {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("group");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, true);
            eAnnotation.getDetails().put
                    ("group", getQualifiedName(getNamespace(eStructuralFeature.getEContainingClass().getEPackage()), group));
        }
        getExtendedMetaData(eStructuralFeature).setGroup(group);
    }

    public EStructuralFeature getAffiliation(EStructuralFeature eStructuralFeature) {
        return getExtendedMetaData(eStructuralFeature).getAffiliation();
    }

    protected EStructuralFeature basicGetAffiliation(EStructuralFeature eStructuralFeature) {
        EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
        if (eAnnotation != null) {
            String qualifiedName = eAnnotation.getDetails().get("affiliation");
            if (qualifiedName != null) {
                int fragmentIndex = qualifiedName.lastIndexOf('#');
                if (fragmentIndex == -1) {
                    return getElement(getNamespace(eStructuralFeature.getEContainingClass().getEPackage()), qualifiedName);
                } else if (fragmentIndex == 0) {
                    return getElement(null, qualifiedName.substring(1));
                } else {
                    return getElement(qualifiedName.substring(0, fragmentIndex), qualifiedName.substring(fragmentIndex + 1));
                }
            }
        }
        return null;
    }

    public void setAffiliation(EStructuralFeature eStructuralFeature, EStructuralFeature affiliation) {
        if (affiliation == null) {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("affiliation");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eStructuralFeature, true);
            eAnnotation.getDetails().put
                    ("affiliation", getQualifiedName(getNamespace(eStructuralFeature.getEContainingClass().getEPackage()), affiliation));
        }
        getExtendedMetaData(eStructuralFeature).setAffiliation(affiliation);
    }

    public EStructuralFeature getAffiliation(EClass eClass, EStructuralFeature eStructuralFeature) {
        if (eClass.getFeatureID(eStructuralFeature) >= 0) {
            return eStructuralFeature;
        }

        switch (getFeatureKind(eStructuralFeature)) {
            case ATTRIBUTE_FEATURE: {
                if (isDocumentRoot(eStructuralFeature.getEContainingClass())) {
                    String namespace = getNamespace(eStructuralFeature);
                    String name = getName(eStructuralFeature);
                    EStructuralFeature result = getLocalAttribute(eClass, namespace, name);
                    if (result != null) {
                        return result;
                    }

                    List<EStructuralFeature> allAttributes = getAllAttributes(eClass);
                    for (int i = 0, size = allAttributes.size(); i < size; ++i) {
                        result = allAttributes.get(i);
                        if (matches(getWildcards(result), namespace)) {
                            return result;
                        }
                    }
                }
                return null;
            }
            case ELEMENT_FEATURE: {
                if (isDocumentRoot(eStructuralFeature.getEContainingClass())) {
                    for (EStructuralFeature affiliation = eStructuralFeature; affiliation != null; affiliation = getAffiliation(affiliation)) {
                        String namespace = getNamespace(affiliation);
                        String name = getName(affiliation);
                        EStructuralFeature result = getLocalElement(eClass, namespace, name);
                        if (result != null) {
                            return result;
                        }
                    }

                    String namespace = getNamespace(eStructuralFeature);
                    if (XMLTypePackage.eNS_URI.equals(namespace)) {
                        return getMixedFeature(eClass);
                    } else {
                        List<EStructuralFeature> allElements = getAllElements(eClass);
                        for (int i = 0, size = allElements.size(); i < size; ++i) {
                            EStructuralFeature result = allElements.get(i);
                            if (matches(getWildcards(result), namespace)) {
                                return result;
                            }
                        }
                    }
                }
                return null;
            }
            default: {
                return null;
            }
        }
    }

    public EStructuralFeature getAttributeWildcardAffiliation(EClass eClass, String namespace, String name) {
        List<EStructuralFeature> allAttributes = getAllAttributes(eClass);
        for (int i = 0, size = allAttributes.size(); i < size; ++i) {
            EStructuralFeature result = allAttributes.get(i);
            if (matches(getWildcards(result), namespace)) {
                return result;
            }
        }

        return null;
    }

    public EStructuralFeature getElementWildcardAffiliation(EClass eClass, String namespace, String name) {
        List<EStructuralFeature> allElements = getAllElements(eClass);
        for (int i = 0, size = allElements.size(); i < size; ++i) {
            EStructuralFeature result = allElements.get(i);
            if (matches(getWildcards(result), namespace)) {
                return result;
            }
        }

        return null;
    }

    public boolean matches(List<String> wildcards, String namespace) {
        if (!wildcards.isEmpty()) {
            for (int i = 0, size = wildcards.size(); i < size; ++i) {
                String wildcard = wildcards.get(i);
                if (matches(wildcard, namespace)) {
                    return true;
                }
            }
        }

        return false;
    }

    public boolean matches(String wildcard, String namespace) {
        return
                wildcard == null ?
                        namespace == null :
                        wildcard.startsWith("!##") ?
                                namespace != null &&
                                        (!wildcard.endsWith(namespace) || wildcard.length() != namespace.length() + 3) &&
                                        !XMLTypePackage.eNS_URI.equals(namespace) :
                                wildcard.equals("##any") && !XMLTypePackage.eNS_URI.equals(namespace) || wildcard.equals(namespace);
    }

    public int getWhiteSpaceFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getWhiteSpaceFacet();
    }

    protected int basicGetWhiteSpaceFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String whiteSpaceLiteral = eAnnotation.getDetails().get("whiteSpace");
            for (int i = 1; i < WHITE_SPACE_KINDS.length; ++i) {
                if (WHITE_SPACE_KINDS[i].equals(whiteSpaceLiteral)) {
                    return i;
                }
            }
        }
        return UNSPECIFIED_WHITE_SPACE;
    }

    public void setWhiteSpaceFacet(EDataType eDataType, int whiteSpace) {
        if (whiteSpace == UNSPECIFIED_WHITE_SPACE) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("whiteSpace");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("whiteSpace", WHITE_SPACE_KINDS[whiteSpace]);
        }
        getExtendedMetaData(eDataType).setWhiteSpaceFacet(whiteSpace);
    }

    public List<String> getEnumerationFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getEnumerationFacet();
    }

    protected List<String> basicGetEnumerationFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String enumerationLiteral = eAnnotation.getDetails().get("enumeration");
            if (enumerationLiteral != null) {
                List<String> result = new ArrayList<String>();
                for (String item : enumerationLiteral.split(WORD_SPLIT_REGEXP)) {
                    String enumeration = replace(replace(item, "%20", " "), "%25", "%");
                    result.add(enumeration);
                }
                return result;
            }
        }
        return Collections.emptyList();
    }

    public void setEnumerationFacet(EDataType eDataType, List<String> literals) {
        if (literals.isEmpty()) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("enumeration");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            StringBuffer result = new StringBuffer();
            for (int i = 0, size = literals.size(); i < size; ++i) {
                result.append(replace(replace(literals.get(i), "%", "%25"), " ", "%20"));
                result.append(' ');
            }
            eAnnotation.getDetails().put("enumeration", result.substring(0, result.length() - 1));
        }
        getExtendedMetaData(eDataType).setEnumerationFacet(literals);
    }

    public List<String> getPatternFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getPatternFacet();
    }

    protected List<String> basicGetPatternFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String patternLiteral = eAnnotation.getDetails().get("pattern");
            if (patternLiteral != null) {
                List<String> result = new ArrayList<String>();
                for (String item : patternLiteral.split(WORD_SPLIT_REGEXP)) {
                    String pattern = replace(replace(item, "%20", " "), "%25", "%");
                    result.add(pattern);
                }
                return result;
            }
        }
        return Collections.emptyList();
    }

    public void setPatternFacet(EDataType eDataType, List<String> pattern) {
        if (pattern.isEmpty()) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("pattern");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            StringBuffer result = new StringBuffer();
            for (int i = 0, size = pattern.size(); i < size; ++i) {
                result.append(replace(replace(pattern.get(i), "%", "%25"), " ", "%20"));
                result.append(' ');
            }
            eAnnotation.getDetails().put("pattern", result.substring(0, result.length() - 1));
        }
        getExtendedMetaData(eDataType).setPatternFacet(pattern);
    }

    public int getTotalDigitsFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getTotalDigitsFacet();
    }

    protected int basicGetTotalDigitsFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String totalDigitsLiteral = eAnnotation.getDetails().get("totalDigits");
            if (totalDigitsLiteral != null) {
                return Integer.parseInt(totalDigitsLiteral);
            }
        }
        return -1;
    }

    public void setTotalDigitsFacet(EDataType eDataType, int digits) {
        if (digits == -1) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("totalDigits");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("totalDigits", Integer.toString(digits));
        }
        getExtendedMetaData(eDataType).setTotalDigitsFacet(digits);
    }

    public int getFractionDigitsFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getFractionDigitsFacet();
    }

    protected int basicGetFractionDigitsFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String fractionDigitsLiteral = eAnnotation.getDetails().get("fractionDigits");
            if (fractionDigitsLiteral != null) {
                return Integer.parseInt(fractionDigitsLiteral);
            }
        }
        return -1;
    }

    public void setFractionDigitsFacet(EDataType eDataType, int digits) {
        if (digits == -1) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("fractionDigits");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("fractionDigits", Integer.toString(digits));
        }
        getExtendedMetaData(eDataType).setFractionDigitsFacet(digits);
    }

    public int getLengthFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getLengthFacet();
    }

    protected int basicGetLengthFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String lengthLiteral = eAnnotation.getDetails().get("length");
            if (lengthLiteral != null) {
                return Integer.parseInt(lengthLiteral);
            }
        }
        return -1;
    }

    public void setLengthFacet(EDataType eDataType, int length) {
        if (length == -1) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("length");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("length", Integer.toString(length));
        }
        getExtendedMetaData(eDataType).setLengthFacet(length);
    }

    public int getMinLengthFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getMinLengthFacet();
    }

    protected int basicGetMinLengthFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String minLengthLiteral = eAnnotation.getDetails().get("minLength");
            if (minLengthLiteral != null) {
                return Integer.parseInt(minLengthLiteral);
            }
        }
        return -1;
    }

    public void setMinLengthFacet(EDataType eDataType, int length) {
        if (length == -1) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("minLength");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("minLength", Integer.toString(length));
        }
        getExtendedMetaData(eDataType).setMinLengthFacet(length);
    }

    public int getMaxLengthFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getMaxLengthFacet();
    }

    protected int basicGetMaxLengthFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        if (eAnnotation != null) {
            String maxLengthLiteral = eAnnotation.getDetails().get("maxLength");
            if (maxLengthLiteral != null) {
                return Integer.parseInt(maxLengthLiteral);
            }
        }
        return -1;
    }

    public void setMaxLengthFacet(EDataType eDataType, int length) {
        if (length == -1) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("maxLength");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("maxLength", Integer.toString(length));
        }
        getExtendedMetaData(eDataType).setMaxLengthFacet(length);
    }

    public String getMinExclusiveFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getMinExclusiveFacet();
    }

    protected String basicGetMinExclusiveFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        return
                eAnnotation == null ?
                        null :
                        (String) eAnnotation.getDetails().get("minExclusive");
    }

    public void setMinExclusiveFacet(EDataType eDataType, String literal) {
        if (literal == null) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("minExclusive");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("minExclusive", literal);
        }
        getExtendedMetaData(eDataType).setMinExclusiveFacet(literal);
    }

    public String getMaxExclusiveFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getMaxExclusiveFacet();
    }

    protected String basicGetMaxExclusiveFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        return
                eAnnotation == null ?
                        null :
                        (String) eAnnotation.getDetails().get("maxExclusive");
    }

    public void setMaxExclusiveFacet(EDataType eDataType, String literal) {
        if (literal == null) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("maxExclusive");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("maxExclusive", literal);
        }
        getExtendedMetaData(eDataType).setMaxExclusiveFacet(literal);
    }

    public String getMinInclusiveFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getMinInclusiveFacet();
    }

    protected String basicGetMinInclusiveFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        return
                eAnnotation == null ?
                        null :
                        (String) eAnnotation.getDetails().get("minInclusive");
    }

    public void setMinInclusiveFacet(EDataType eDataType, String literal) {
        if (literal == null) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("minInclusive");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("minInclusive", literal);
        }
        getExtendedMetaData(eDataType).setMinInclusiveFacet(literal);
    }

    public String getMaxInclusiveFacet(EDataType eDataType) {
        return getExtendedMetaData(eDataType).getMaxInclusiveFacet();
    }

    protected String basicGetMaxInclusiveFacet(EDataType eDataType) {
        EAnnotation eAnnotation = getAnnotation(eDataType, false);
        return
                eAnnotation == null ?
                        null :
                        (String) eAnnotation.getDetails().get("maxInclusive");
    }

    public void setMaxInclusiveFacet(EDataType eDataType, String literal) {
        if (literal == null) {
            EAnnotation eAnnotation = getAnnotation(eDataType, false);
            if (eAnnotation != null) {
                eAnnotation.getDetails().remove("maxInclusive");
            }
        } else {
            EAnnotation eAnnotation = getAnnotation(eDataType, true);
            eAnnotation.getDetails().put("maxInclusive", literal);
        }
        getExtendedMetaData(eDataType).setMaxInclusiveFacet(literal);
    }

    public EPackage demandPackage(String namespace) {
        EPackage ePackage = demandRegistry.getEPackage(namespace);
        if (ePackage == null) {
            ePackage = EcoreFactory.eINSTANCE.createEPackage();
            ePackage.setNsURI(namespace);
            setQualified(ePackage, namespace != null);
            if (namespace != null) {
                ePackage.setNsPrefix
                        (namespace.equals(ExtendedMetaData.XMLNS_URI) ?
                                 namespace.equals(ExtendedMetaData.XML_URI) ?
                                         "xml" :
                                         "xmlns" :
                                 computePrefix(namespace));
            }
            demandRegistry.put(namespace, ePackage);

            // demandDocumentRoot(ePackage);

            EClass documentRootEClass = EcoreFactory.eINSTANCE.createEClass();
            documentRootEClass.getESuperTypes().add(XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot());
            documentRootEClass.setName("DocumentRoot");
            ePackage.getEClassifiers().add(documentRootEClass);
            setDocumentRoot(documentRootEClass);
        }
        return ePackage;
    }

    public static String computePrefix(String namespace) {
        int index = namespace.length();
        boolean containsLetter = false;
        StringBuffer prefix = new StringBuffer(index);
        while (--index >= 0) {
            char character = namespace.charAt(index);
            if (XMLTypeUtil.isNCNamePart(character)) {
                prefix.append(character);
                containsLetter = Character.isLetter(character);
                break;
            }
        }
        while (--index >= 0) {
            char character = namespace.charAt(index);
            if (XMLTypeUtil.isNCNamePart(character)) {
                prefix.append(character);
                if (!containsLetter) {
                    containsLetter = Character.isLetter(character);
                }
            } else if (!containsLetter) {
                prefix.append('_');
            } else {
                break;
            }
        }

        int length = prefix.length();
        if (length == 0 || !XMLTypeUtil.isNCNameStart(prefix.charAt(length - 1))) {
            prefix.append('_');
        }
        // Revert the "prefix" string.
        StringBuffer result = new StringBuffer(prefix.length());
        for (int i = prefix.length() - 1; i >= 0; --i) {
            result.append(prefix.charAt(i));
        }
        return result.toString();
    }

    public EClassifier demandType(String namespace, String name) {
        EPackage ePackage = demandPackage(namespace);
        EClassifier eClassifier = getType(ePackage, name);
        if (eClassifier != null) {
            return eClassifier;
        } else {
            EClass eClass = EcoreFactory.eINSTANCE.createEClass();
            eClass.setName(name);
            eClass.getESuperTypes().add(XMLTypePackage.eINSTANCE.getAnyType());
            setContentKind(eClass, MIXED_CONTENT);
            ePackage.getEClassifiers().add(eClass);
            return eClass;
        }
    }

    public EStructuralFeature demandFeature(String namespace, String name, boolean isElement) {
        return demandFeature(namespace, name, isElement, isElement);
    }

    public EStructuralFeature demandFeature(String namespace, String name, boolean isElement, boolean isReference) {
        EPackage ePackage = demandPackage(namespace);
        EClass documentRootEClass = getDocumentRoot(ePackage);
        EStructuralFeature eStructuralFeature =
                isElement ?
                        getLocalElement(documentRootEClass, namespace, name) :
                        getLocalAttribute(documentRootEClass, namespace, name);
        if (eStructuralFeature != null) {
            return eStructuralFeature;
        } else {
            if (isReference) {
                EReference eReference = EcoreFactory.eINSTANCE.createEReference();
                if (isElement) {
                    eReference.setContainment(true);
                    eReference.setResolveProxies(false);
                }
                eReference.setEType(EcorePackage.Literals.EOBJECT);
                eReference.setName(name);
                eReference.setDerived(true);
                eReference.setTransient(true);
                eReference.setVolatile(true);
                documentRootEClass.getEStructuralFeatures().add(eReference);

                setFeatureKind(eReference, isElement ? ELEMENT_FEATURE : ATTRIBUTE_FEATURE);
                setNamespace(eReference, namespace);

                // Mark the bound as unspecified so that it won't be considered many
                // but can nevertheless be recognized as being unspecified and perhaps still be treat as many.
                //
                if (isElement) {
                    eReference.setUpperBound(ETypedElement.UNSPECIFIED_MULTIPLICITY);
                }

                return eReference;
            } else {
                EAttribute eAttribute = EcoreFactory.eINSTANCE.createEAttribute();
                eAttribute.setName(name);
                eAttribute.setEType(XMLTypePackage.eINSTANCE.getAnySimpleType());
                eAttribute.setDerived(true);
                eAttribute.setTransient(true);
                eAttribute.setVolatile(true);
                documentRootEClass.getEStructuralFeatures().add(eAttribute);

                setFeatureKind(eAttribute, isElement ? ELEMENT_FEATURE : ATTRIBUTE_FEATURE);
                setNamespace(eAttribute, namespace);

                // Mark the bound as unspecified so that it won't be considered many
                // but can nevertheless be recognized as being unspecified and perhaps still be treat as many.
                //
                if (isElement) {
                    eAttribute.setUpperBound(ETypedElement.UNSPECIFIED_MULTIPLICITY);
                }

                return eAttribute;
            }
        }
    }

    @SuppressWarnings("unchecked")
    public Collection<EPackage> demandedPackages() {
        return (Collection<EPackage>) (Collection<?>) demandRegistry.values();
    }

    public static interface EPackageExtendedMetaData {

        interface Holder {

            EPackageExtendedMetaData getExtendedMetaData();

            void setExtendedMetaData(EPackageExtendedMetaData ePackageExtendedMetaData);
        }

        boolean isQualified();

        void setQualified(boolean isQualified);

        EClassifier getType(String name);

        void rename(EClassifier eClassifier, String newName);
    }

    public class EPackageExtendedMetaDataImpl implements EPackageExtendedMetaData {

        protected EPackage ePackage;
        protected boolean isInitialized;
        protected boolean isQualified;
        protected Map<String, EClassifier> nameToClassifierMap;

        public EPackageExtendedMetaDataImpl(EPackage ePackage) {
            this.ePackage = ePackage;
        }

        public boolean isQualified() {
            if (!isInitialized) {
                setQualified(basicIsQualified(ePackage));
            }
            return isQualified;
        }

        public void setQualified(boolean isQualified) {
            this.isQualified = isQualified;
            isInitialized = true;
        }

        public EClassifier getType(String name) {
            EClassifier result = null;
            if (nameToClassifierMap != null) {
                result = nameToClassifierMap.get(name);
            }
            if (result == null) {
                List<EClassifier> eClassifiers = ePackage.getEClassifiers();
                int size = eClassifiers.size();
                if (nameToClassifierMap == null || nameToClassifierMap.size() != size) {
                    Map<String, EClassifier> nameToClassifierMap = new HashMap<String, EClassifier>();
                    if (this.nameToClassifierMap != null) {
                        nameToClassifierMap.putAll(this.nameToClassifierMap);
                    }

                    // For demand created created packages we allow the list of classifiers to grow
                    // so this should handle those additional instances.
                    //
                    int originalMapSize = nameToClassifierMap.size();
                    for (int i = originalMapSize; i < size; ++i) {
                        EClassifier eClassifier = eClassifiers.get(i);
                        String eClassifierName = getName(eClassifier);
                        EClassifier conflictingEClassifier = nameToClassifierMap.put(eClassifierName, eClassifier);
                        if (conflictingEClassifier != null && conflictingEClassifier != eClassifier) {
                            nameToClassifierMap.put(eClassifierName, conflictingEClassifier);
                        }
                    }

                    if (nameToClassifierMap.size() != size) {
                        for (int i = 0; i < originalMapSize; ++i) {
                            EClassifier eClassifier = eClassifiers.get(i);
                            String eClassifierName = getName(eClassifier);
                            EClassifier conflictingEClassifier = nameToClassifierMap.put(eClassifierName, eClassifier);
                            if (conflictingEClassifier != null && conflictingEClassifier != eClassifier) {
                                nameToClassifierMap.put(eClassifierName, conflictingEClassifier);
                            }
                        }
                    }
                    result = nameToClassifierMap.get(name);
                    this.nameToClassifierMap = nameToClassifierMap;
                }
            }

            return result;
        }

        public void rename(EClassifier eClassifier, String newName) {
            if (nameToClassifierMap != null) {
                nameToClassifierMap.values().remove(eClassifier);
                nameToClassifierMap.put(newName, eClassifier);
            }
        }
    }

    protected EPackageExtendedMetaData getExtendedMetaData(EPackage ePackage) {
        if (extendedMetaDataHolderCache != null) {
            EPackageExtendedMetaData result = (EPackageExtendedMetaData) extendedMetaDataHolderCache.get(ePackage);
            if (result == null) {
                extendedMetaDataHolderCache.put(ePackage, result = createEPackageExtendedMetaData(ePackage));
            }
            return result;
        } else {
            EPackageExtendedMetaData.Holder holder = (EPackageExtendedMetaData.Holder) ePackage;
            EPackageExtendedMetaData result = holder.getExtendedMetaData();
            if (result == null) {
                holder.setExtendedMetaData(result = createEPackageExtendedMetaData(ePackage));
            }
            return result;
        }
    }

    protected EPackageExtendedMetaData createEPackageExtendedMetaData(EPackage ePackage) {
        return new EPackageExtendedMetaDataImpl(ePackage);
    }

    protected static final String UNINITIALIZED_STRING = "uninitialized";
    protected static final int UNINITIALIZED_INT = -2;
    protected static final EDataType UNINITIALIZED_EDATA_TYPE = EcoreFactory.eINSTANCE.createEDataType();
    protected static final EStructuralFeature UNINITIALIZED_ESTRUCTURAL_FEATURE = EcoreFactory.eINSTANCE.createEAttribute();

    public static interface EClassifierExtendedMetaData {

        interface Holder {

            EClassifierExtendedMetaData getExtendedMetaData();

            void setExtendedMetaData(EClassifierExtendedMetaData eClassifierExtendedMetaData);
        }

        String getName();

        void setName(String name);

        int getContentKind();

        void setContentKind(int kind);

        int getDerivationKind();

        EDataType getBaseType();

        void setBaseType(EDataType baseType);

        EDataType getItemType();

        void setItemType(EDataType itemType);

        List<EDataType> getMemberTypes();

        void setMemberTypes(List<EDataType> memberTypes);

        int getWhiteSpaceFacet();

        void setWhiteSpaceFacet(int whiteSpace);

        List<String> getEnumerationFacet();

        void setEnumerationFacet(List<String> literals);

        List<String> getPatternFacet();

        void setPatternFacet(List<String> literals);

        int getTotalDigitsFacet();

        void setTotalDigitsFacet(int digits);

        int getFractionDigitsFacet();

        void setFractionDigitsFacet(int digits);

        int getLengthFacet();

        void setLengthFacet(int length);

        int getMinLengthFacet();

        void setMinLengthFacet(int length);

        int getMaxLengthFacet();

        void setMaxLengthFacet(int length);

        String getMinExclusiveFacet();

        void setMinExclusiveFacet(String literal);

        String getMaxExclusiveFacet();

        void setMaxExclusiveFacet(String literal);

        String getMinInclusiveFacet();

        void setMinInclusiveFacet(String literal);

        String getMaxInclusiveFacet();

        void setMaxInclusiveFacet(String literal);
    }

    public class EClassExtendedMetaDataImpl implements EClassifierExtendedMetaData {

        protected EClass eClass;
        protected String name = UNINITIALIZED_STRING;
        protected int contentKind = UNINITIALIZED_INT;

        public EClassExtendedMetaDataImpl(EClass eClass) {
            this.eClass = eClass;
        }

        public String getName() {
            if (name == UNINITIALIZED_STRING) {
                setName(basicGetName(eClass));
            }
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getContentKind() {
            if (contentKind == UNINITIALIZED_INT) {
                setContentKind(basicGetContentKind(eClass));
            }
            return contentKind;
        }

        public void setContentKind(int kind) {
            this.contentKind = kind;
        }

        public int getDerivationKind() {
            return 0;
        }

        public EDataType getBaseType() {
            return null;
        }

        public void setBaseType(EDataType baseType) {
            throw new UnsupportedOperationException("Can't set the base type of an EClass");
        }

        public EDataType getItemType() {
            return null;
        }

        public void setItemType(EDataType itemType) {
            throw new UnsupportedOperationException("Can't set the item type of an EClass");
        }

        public List<EDataType> getMemberTypes() {
            return Collections.emptyList();
        }

        public void setMemberTypes(List<EDataType> memberTypes) {
            throw new UnsupportedOperationException("Can't set the member types of an EClass");
        }

        public int getWhiteSpaceFacet() {
            return 0;
        }

        public void setWhiteSpaceFacet(int whiteSpace) {
            throw new UnsupportedOperationException("Can't set the white space of an EClass");
        }

        public List<String> getEnumerationFacet() {
            return Collections.emptyList();
        }

        public void setEnumerationFacet(List<String> literals) {
            throw new UnsupportedOperationException("Can't set the enumeration of an EClass");
        }

        public List<String> getPatternFacet() {
            return Collections.emptyList();
        }

        public void setPatternFacet(List<String> pattern) {
            throw new UnsupportedOperationException("Can't set the pattern of an EClass");
        }

        public int getTotalDigitsFacet() {
            return -1;
        }

        public void setTotalDigitsFacet(int digits) {
            throw new UnsupportedOperationException("Can't set the total digits of an EClass");
        }

        public int getFractionDigitsFacet() {
            return -1;
        }

        public void setFractionDigitsFacet(int digits) {
            throw new UnsupportedOperationException("Can't set the fraction digits of an EClass");
        }

        public int getLengthFacet() {
            return -1;
        }

        public void setLengthFacet(int length) {
            throw new UnsupportedOperationException("Can't set the length of an EClass");
        }

        public int getMinLengthFacet() {
            return -1;
        }

        public void setMinLengthFacet(int minLength) {
            throw new UnsupportedOperationException("Can't set the min length of an EClass");
        }

        public int getMaxLengthFacet() {
            return -1;
        }

        public void setMaxLengthFacet(int maxLength) {
            throw new UnsupportedOperationException("Can't set the max length of an EClass");
        }

        public String getMinExclusiveFacet() {
            return null;
        }

        public void setMinExclusiveFacet(String literal) {
            throw new UnsupportedOperationException("Can't set the min exclusive of an EClass");
        }

        public String getMaxExclusiveFacet() {
            return null;
        }

        public void setMaxExclusiveFacet(String literal) {
            throw new UnsupportedOperationException("Can't set the max exclusive of an EClass");
        }

        public String getMinInclusiveFacet() {
            return null;
        }

        public void setMinInclusiveFacet(String literal) {
            throw new UnsupportedOperationException("Can't set the min inclusive of an EClass");
        }

        public String getMaxInclusiveFacet() {
            return null;
        }

        public void setMaxInclusiveFacet(String literal) {
            throw new UnsupportedOperationException("Can't set the max inclusive of an EClass");
        }
    }

    public class EDataTypeExtendedMetaDataImpl implements EClassifierExtendedMetaData {

        protected EDataType eDataType;
        protected String name = UNINITIALIZED_STRING;
        protected EDataType baseType = UNINITIALIZED_EDATA_TYPE;
        protected EDataType itemType = UNINITIALIZED_EDATA_TYPE;
        protected List<EDataType> memberTypes;
        protected int derivationKind = UNINITIALIZED_INT;
        protected int whiteSpace = UNINITIALIZED_INT;
        protected List<String> enumerationLiterals;
        protected List<String> pattern;
        int totalDigits = UNINITIALIZED_INT;
        int fractionDigits = UNINITIALIZED_INT;
        int length = UNINITIALIZED_INT;
        int minLength = UNINITIALIZED_INT;
        int maxLength = UNINITIALIZED_INT;
        String minExclusive = UNINITIALIZED_STRING;
        String maxExclusive = UNINITIALIZED_STRING;
        String minInclusive = UNINITIALIZED_STRING;
        String maxInclusive = UNINITIALIZED_STRING;

        public EDataTypeExtendedMetaDataImpl(EDataType eDataType) {
            this.eDataType = eDataType;
        }

        public String getName() {
            if (name == UNINITIALIZED_STRING) {
                setName(basicGetName(eDataType));
            }
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getContentKind() {
            return 0;
        }

        public void setContentKind(int kind) {
            throw new UnsupportedOperationException("Can't set the content kind of an EDataType");
        }

        public int getDerivationKind() {
            if (derivationKind == UNINITIALIZED_INT) {
                if (getBaseType() != null) {
                    derivationKind = RESTRICTION_DERIVATION;
                } else if (getItemType() != null) {
                    derivationKind = LIST_DERIVATION;
                } else if (!getMemberTypes().isEmpty()) {
                    derivationKind = UNION_DERIVATION;
                } else {
                    derivationKind = 0;
                }
            }
            return derivationKind;
        }

        public EDataType getBaseType() {
            if (baseType == UNINITIALIZED_EDATA_TYPE) {
                setBaseType(basicGetBaseType(eDataType));
            }
            return baseType;
        }

        public void setBaseType(EDataType baseType) {
            this.baseType = baseType;
            derivationKind = UNINITIALIZED_INT;
        }

        public EDataType getItemType() {
            if (itemType == UNINITIALIZED_EDATA_TYPE) {
                setItemType(basicGetItemType(eDataType));
            }
            return itemType;
        }

        public void setItemType(EDataType itemType) {
            this.itemType = itemType;
            derivationKind = UNINITIALIZED_INT;
        }

        public List<EDataType> getMemberTypes() {
            if (memberTypes == null) {
                setMemberTypes(basicGetMemberTypes(eDataType));
            }
            return memberTypes;
        }

        public void setMemberTypes(List<EDataType> memberTypes) {
            this.memberTypes = memberTypes;
            derivationKind = UNINITIALIZED_INT;
        }

        public int getWhiteSpaceFacet() {
            if (whiteSpace == UNINITIALIZED_INT) {
                setWhiteSpaceFacet(basicGetWhiteSpaceFacet(eDataType));
            }
            return whiteSpace;
        }

        public void setWhiteSpaceFacet(int whiteSpace) {
            this.whiteSpace = whiteSpace;
        }

        public List<String> getEnumerationFacet() {
            if (enumerationLiterals == null) {
                setEnumerationFacet(basicGetEnumerationFacet(eDataType));
            }
            return enumerationLiterals;
        }

        public void setEnumerationFacet(List<String> literals) {
            this.enumerationLiterals = literals;
        }

        public List<String> getPatternFacet() {
            if (pattern == null) {
                setPatternFacet(basicGetPatternFacet(eDataType));
            }
            return pattern;
        }

        public void setPatternFacet(List<String> pattern) {
            this.pattern = pattern;
        }

        public int getTotalDigitsFacet() {
            if (totalDigits == UNINITIALIZED_INT) {
                setTotalDigitsFacet(basicGetTotalDigitsFacet(eDataType));
            }
            return totalDigits;
        }

        public void setTotalDigitsFacet(int digits) {
            this.totalDigits = digits;
        }

        public int getFractionDigitsFacet() {
            if (fractionDigits == UNINITIALIZED_INT) {
                setFractionDigitsFacet(basicGetFractionDigitsFacet(eDataType));
            }
            return fractionDigits;
        }

        public void setFractionDigitsFacet(int digits) {
            this.fractionDigits = digits;
        }

        public int getLengthFacet() {
            if (length == UNINITIALIZED_INT) {
                setLengthFacet(basicGetLengthFacet(eDataType));
            }
            return length;
        }

        public void setLengthFacet(int length) {
            this.length = length;
        }

        public int getMinLengthFacet() {
            if (minLength == UNINITIALIZED_INT) {
                setMinLengthFacet(basicGetMinLengthFacet(eDataType));
            }
            return minLength;
        }

        public void setMinLengthFacet(int minLength) {
            this.minLength = minLength;
        }

        public int getMaxLengthFacet() {
            if (maxLength == UNINITIALIZED_INT) {
                setMaxLengthFacet(basicGetMaxLengthFacet(eDataType));
            }
            return maxLength;
        }

        public void setMaxLengthFacet(int maxLength) {
            this.maxLength = maxLength;
        }

        public String getMinExclusiveFacet() {
            if (minExclusive == UNINITIALIZED_STRING) {
                setMinExclusiveFacet(basicGetMinExclusiveFacet(eDataType));
            }
            return minExclusive;
        }

        public void setMinExclusiveFacet(String literal) {
            this.minExclusive = literal;
        }

        public String getMaxExclusiveFacet() {
            if (maxExclusive == UNINITIALIZED_STRING) {
                setMaxExclusiveFacet(basicGetMaxExclusiveFacet(eDataType));
            }
            return maxExclusive;
        }

        public void setMaxExclusiveFacet(String literal) {
            this.maxExclusive = literal;
        }

        public String getMinInclusiveFacet() {
            if (minInclusive == UNINITIALIZED_STRING) {
                setMinInclusiveFacet(basicGetMinInclusiveFacet(eDataType));
            }
            return minInclusive;
        }

        public void setMinInclusiveFacet(String literal) {
            this.minInclusive = literal;
        }

        public String getMaxInclusiveFacet() {
            if (maxInclusive == UNINITIALIZED_STRING) {
                setMaxInclusiveFacet(basicGetMaxInclusiveFacet(eDataType));
            }
            return maxInclusive;
        }

        public void setMaxInclusiveFacet(String literal) {
            this.maxInclusive = literal;
        }
    }

    protected EClassifierExtendedMetaData getExtendedMetaData(EClassifier eClassifier) {
        if (extendedMetaDataHolderCache != null) {
            EClassifierExtendedMetaData result = (EClassifierExtendedMetaData) extendedMetaDataHolderCache.get(eClassifier);
            if (result == null) {
                extendedMetaDataHolderCache.put(eClassifier, result = createEClassifierExtendedMetaData(eClassifier));
            }
            return result;
        } else {
            EClassifierExtendedMetaData.Holder holder = (EClassifierExtendedMetaData.Holder) eClassifier;
            EClassifierExtendedMetaData result = holder.getExtendedMetaData();
            if (result == null) {
                holder.setExtendedMetaData(result = createEClassifierExtendedMetaData(eClassifier));
            }
            return result;
        }
    }

    protected EClassifierExtendedMetaData createEClassifierExtendedMetaData(EClassifier eClassifier) {
        if (eClassifier instanceof EClass) {
            return new EClassExtendedMetaDataImpl((EClass) eClassifier);
        } else {
            return new EDataTypeExtendedMetaDataImpl((EDataType) eClassifier);
        }
    }

    public static interface EStructuralFeatureExtendedMetaData {

        interface Holder {

            EStructuralFeatureExtendedMetaData getExtendedMetaData();

            void setExtendedMetaData(EStructuralFeatureExtendedMetaData eStructuralFeatureExtendedMetaData);
        }

        String getName();

        void setName(String name);

        String getNamespace();

        void setNamespace(String namespace);

        int getFeatureKind();

        void setFeatureKind(int kind);

        List<String> getWildcards();

        void setWildcards(List<String> wildcards);

        int getProcessingKind();

        void setProcessingKind(int kind);

        EStructuralFeature getGroup();

        void setGroup(EStructuralFeature group);

        EStructuralFeature getAffiliation();

        void setAffiliation(EStructuralFeature affiliation);

        Map<EClass, FeatureMapUtil.Validator> getValidatorMap();
    }

    public class EStructuralFeatureExtendedMetaDataImpl implements EStructuralFeatureExtendedMetaData {

        protected EStructuralFeature eStructuralFeature;
        protected String name = UNINITIALIZED_STRING;
        protected String namespace = UNINITIALIZED_STRING;
        protected int featureKind = UNINITIALIZED_INT;
        protected List<String> wildcards;
        protected int processingKind = UNINITIALIZED_INT;
        protected EStructuralFeature group = UNINITIALIZED_ESTRUCTURAL_FEATURE;
        protected EStructuralFeature affiliation = UNINITIALIZED_ESTRUCTURAL_FEATURE;
        protected Map<EClass, FeatureMapUtil.Validator> validatorMap;

        public EStructuralFeatureExtendedMetaDataImpl(EStructuralFeature eStructuralFeature) {
            this.eStructuralFeature = eStructuralFeature;
        }

        public Map<EClass, FeatureMapUtil.Validator> getValidatorMap() {
            if (validatorMap == null) {
                validatorMap = new HashMap<EClass, FeatureMapUtil.Validator>();
            }
            return validatorMap;
        }

        public String getName() {
            if (name == UNINITIALIZED_STRING) {
                setName(basicGetName(eStructuralFeature));
            }
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getNamespace() {
            if (namespace == UNINITIALIZED_STRING) {
                setNamespace(basicGetNamespace(eStructuralFeature));
            }
            return namespace;
        }

        public void setNamespace(String namespace) {
            this.namespace = namespace;
        }

        public int getFeatureKind() {
            if (featureKind == UNINITIALIZED_INT) {
                setFeatureKind(basicGetFeatureKind(eStructuralFeature));
            }
            return featureKind;
        }

        public void setFeatureKind(int kind) {
            this.featureKind = kind;
        }

        public List<String> getWildcards() {
            if (wildcards == null) {
                setWildcards(basicGetWildcards(eStructuralFeature));
            }
            return wildcards;
        }

        public void setWildcards(List<String> wildcards) {
            this.wildcards = wildcards;
        }

        public int getProcessingKind() {
            if (processingKind == UNINITIALIZED_INT) {
                setProcessingKind(basicGetProcessingKind(eStructuralFeature));
            }
            return processingKind;
        }

        public void setProcessingKind(int kind) {
            this.processingKind = kind;
        }

        public EStructuralFeature getGroup() {
            if (group == UNINITIALIZED_ESTRUCTURAL_FEATURE) {
                setGroup(basicGetGroup(eStructuralFeature));
            }
            return group;
        }

        public void setGroup(EStructuralFeature group) {
            this.group = group;
        }

        public EStructuralFeature getAffiliation() {
            if (affiliation == UNINITIALIZED_ESTRUCTURAL_FEATURE) {
                setAffiliation(basicGetAffiliation(eStructuralFeature));
            }
            return affiliation;
        }

        public void setAffiliation(EStructuralFeature affiliation) {
            this.affiliation = affiliation;
        }
    }

    protected EStructuralFeatureExtendedMetaData getExtendedMetaData(EStructuralFeature eStructuralFeature) {
        if (extendedMetaDataHolderCache != null) {
            EStructuralFeatureExtendedMetaData result = (EStructuralFeatureExtendedMetaData) extendedMetaDataHolderCache.get(eStructuralFeature);
            if (result == null) {
                extendedMetaDataHolderCache.put(eStructuralFeature, result = createEStructuralFeatureExtendedMetaData(eStructuralFeature));
            }
            return result;
        } else {
            EStructuralFeatureExtendedMetaData.Holder holder = (EStructuralFeatureExtendedMetaData.Holder) eStructuralFeature;
            EStructuralFeatureExtendedMetaData result = holder.getExtendedMetaData();
            if (result == null) {
                holder.setExtendedMetaData(result = createEStructuralFeatureExtendedMetaData(eStructuralFeature));
            }
            return result;
        }
    }

    protected EStructuralFeatureExtendedMetaData createEStructuralFeatureExtendedMetaData(EStructuralFeature eStructuralFeature) {
        return new EStructuralFeatureExtendedMetaDataImpl(eStructuralFeature);
    }

    private static String replace(String in, String oldString, String newString) {
        if (in == null || oldString == null) {
            return in;
        }

        int oldStringLength = oldString.length();
        if (oldStringLength == 0) {
            return in;
        }

        if (newString == null) {
            newString = "";
        }
        int newStringLength = newString.length();

        int index = -newStringLength;
        StringBuffer result = new StringBuffer(in);
        while ((index = indexOf(result, oldString, index + newStringLength)) >= 0) {
            result.replace(index, index + oldStringLength, newString);
        }

        return result.toString();
    }

    private static int indexOf(StringBuffer in, String str, int fromIndex) {
        if (in == null) {
            return -1;
        }

        if (str == null) {
            str = "";
        }

        int lengthIn = in.length();
        int lengthStr = str.length();

        if (lengthIn < lengthStr) {
            return -1;
        }

        if (fromIndex > lengthIn) {
            if (lengthIn == 0 && fromIndex == 0 && lengthStr == 0) {
                return 0;
            }
            return -1;
        }

        if (fromIndex < 0) {
            fromIndex = 0;
        }

        if (lengthStr == 0) {
            return fromIndex;
        }

        int strPos = 0;
        for (int i = fromIndex; i < lengthIn; i++) {
            if (in.charAt(i) == str.charAt(strPos)) {
                strPos++;
                if (strPos == lengthStr) {
                    return i - lengthStr + 1;
                }
            } else {
                strPos = 0;
            }
        }

        return -1;
    }
}
